package com.demo.task.download

import android.util.Log
import com.demo.task.MainApplication
import com.demo.task.UniqueArrayBlockingQueue
import com.demo.task.ext.getUrlFileName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.http.GET
import retrofit2.http.Streaming
import retrofit2.http.Url
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.TimeUnit


class DownloadManager private constructor(
    val builder: Builder
) {
    companion object {
        private const val TAG = "DownloadManager"
        private const val TIMEOUT = 5 * 60L
    }

    private var isBeginDown = false
    private var downProgress = -1L
    private val downloadScope by lazy {
        CoroutineScope(Dispatchers.IO)
    }

    private val queue by lazy {
        UniqueArrayBlockingQueue<String>(200)
    }

    var total = 0
        private set

    private val service by lazy {
        val client = OkHttpClient.Builder()
            .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
            .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
            .build()

        Retrofit.Builder()
            .client(client)
            .baseUrl("http://znback.cux.cc/")
            .build()
            .create(DownloadService::class.java)
    }


    @get:JvmName("savePath")
    val savePath: String = builder.savePath


    /**
     * 下载监听
     */
    var listener: DownloadCallback? = null

    /**
     * 下载单个
     */
    fun download(url: String) {
        if (url.isEmpty()) {
            Log.e(TAG, "download: 下载数量不能为空")
            return
        }
        if (!queue.contains(url)) {
            begin()
            if (queue.offer(url)) {
                total++
            }
        }
    }

    /**
     * 批量下载
     */
    fun download(urls: List<String>) {
        if (urls.isEmpty()) {
            Log.e(TAG, "download: 下载数量不能为空")
            return
        }

        urls.forEach { url ->
            download(url)
        }
    }

    /**
     * 清楚下载队列
     */
    fun clear() {
        queue.clear()
    }

    /**
     * 释放所有下载
     */
    fun release() {
        queue.clear()
        downloadScope.cancel()
    }

    private fun begin() {
        if (isBeginDown) return
        isBeginDown = true
        downloadScope.launch {
            while (true) {
                val url = queue.take()
                val fileName = url.getUrlFileName()
                val cacheDir = MainApplication.instance.externalCacheDir
                val file = File(cacheDir, fileName).createFile()

                runCatching {
                    listener?.begin(url)
                    val externalFilesDir = MainApplication.instance.getExternalFilesDir(null)!!.path
                    val path = savePath.ifEmpty { externalFilesDir }.createDir()

                    //查看本地是否已经下载完成
                    val originFile = File(path, fileName)
                    if (originFile.exists()) {
                        Log.i(TAG, "begin 文件已存在: ${originFile.name}")
                        listener?.progress(100)
                        listener?.fileComplete(url)
                        file.delete()
                        return@runCatching
                    }

                    Log.i(TAG, "begin 开始下载: $url")


                    var retryCount = 0 //重试次数

                    while (true) {
                        try {
                            val body = service.download(url)
                            val contentLength = body.contentLength()

                            FileOutputStream(file).use { outputStream ->
                                val bufferSize = 1024 * 1024 * 10 //缓冲区大小
                                val buffer = ByteArray(bufferSize) //缓冲区
                                var currentLength = 0F //当前已下载长度

                                BufferedInputStream(body.byteStream(), bufferSize).use {
                                    var readLength: Int //读取长度
                                    while (it.read(buffer, 0, bufferSize)
                                            .also { readLength = it } != -1
                                    ) {
                                        outputStream.write(buffer, 0, readLength)
                                        currentLength += readLength
                                        val progress =
                                            (currentLength / contentLength * 100).toLong()
                                        if (downProgress != progress) {
                                            downProgress = progress
                                            listener?.progress(progress)
                                        }
                                    }
                                }
                            }
                            break
                        } catch (it: Exception) {
                            retryCount++
                            delay(3_000)

                            if (retryCount > builder.maxRetryTimes) {
                                throw it
                            }
                            Log.w(TAG, "下载异常: 重试 $retryCount 次")
                        }
                    }


                    //保存到配置路径
                    File(path, url.getUrlFileName()).createFile().also {
                        file.renameTo(it)
                    }
                    listener?.fileComplete(url)
                }.onFailure { t ->
                    Log.e(TAG, "begin onFailure: $url", t)
                    file.delete()
                    listener?.failure(url, t)
                }

                //下载完成
                if (queue.isEmpty()) {
                    listener?.complete()
                    total = 0
                }
            }
        }
    }


    class Builder {
        internal var savePath = ""
        internal var maxRetryTimes = 10

        fun savePath(savePath: String) = apply {
            this.savePath = savePath
        }

        fun maxRetryTimes(times: Int) = apply {
            this.maxRetryTimes = times
        }

        fun build(): DownloadManager {
            return DownloadManager(this)
        }
    }

}


interface DownloadService {
    @Streaming
    @GET
    suspend fun download(@Url url: String): ResponseBody
}

interface DownloadCallback {
    fun begin(url: String)
    fun complete()
    fun fileComplete(url: String) // 单个文件下载完成
    fun progress(progress: Long)
    fun failure(url: String, t: Throwable)
}

fun File.createFile() = apply {
    val file = this
    if (file.exists()) {
        file.delete()
    }
    file.createNewFile()
}

fun String.createDir() = apply {
    val file = File(this)
    if (!file.exists()) {
        file.mkdir()
    }
}